﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Diagnostics.CodeAnalysis
Imports System.Reflection
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities.Rules
    Friend Class ParserContext
        ' Methods
        Friend Sub New(ByVal tokens As List([Of] Token))
            Me.exprPositions = New Dictionary([Of] Object, Integer)
            Me.provideIntellisense = True
            Me.tokens = tokens
        End Sub

        Friend Sub New(ByVal expressionString As String)
            Me.exprPositions = New Dictionary([Of] Object, Integer)
            Dim scanner As New Scanner(expressionString)
            Me.tokens = New List([Of] Token)
            scanner.Tokenize(Me.tokens)
        End Sub

        Private Shared Sub AddCandidates(ByVal candidateMethods As List([Of] MemberInfo), ByVal methods As MemberInfo())
            If (Not methods Is Nothing) Then
                Dim i As Integer
                For i = 0 To methods.Length - 1
                    Dim item As MethodInfo = DirectCast(methods(i), MethodInfo)
                    If Not item.IsGenericMethod Then
                        candidateMethods.Add(item)
                    End If
                Next i
            End If
        End Sub

        Friend Shared Function IsNonPrivate(ByVal fieldInfo As FieldInfo, ByVal thisType As Type) As Boolean
            If ((fieldInfo.IsPublic OrElse fieldInfo.IsFamily) OrElse fieldInfo.IsFamilyOrAssembly) Then
                Return True
            End If
            If (Not fieldInfo.IsAssembly AndAlso Not fieldInfo.IsFamilyAndAssembly) Then
                Return False
            End If
            Return (fieldInfo.DeclaringType.Assembly Is thisType.Assembly)
        End Function

        Friend Shared Function IsNonPrivate(ByVal methodInfo As MethodInfo, ByVal thisType As Type) As Boolean
            If ((methodInfo.IsPublic OrElse methodInfo.IsFamily) OrElse methodInfo.IsFamilyOrAssembly) Then
                Return True
            End If
            If (Not methodInfo.IsAssembly AndAlso Not methodInfo.IsFamilyAndAssembly) Then
                Return False
            End If
            Return (methodInfo.DeclaringType.Assembly Is thisType.Assembly)
        End Function

        Friend Shared Function IsNonPrivate(ByVal type As Type, ByVal thisType As Type) As Boolean
            If (type.IsPublic OrElse type.IsNestedPublic) Then
                Return True
            End If
            If ((Not type.IsNestedAssembly AndAlso Not type.IsNestedFamANDAssem) AndAlso Not type.IsNestedFamORAssem) Then
                Return False
            End If
            Return (type.Assembly Is thisType.Assembly)
        End Function

        Friend Function NextToken() As Token
            If (Me.currentToken = (Me.tokens.Count - 1)) Then
                Me.currentToken += 1
                Return Nothing
            End If
            Me.currentToken += 1
            Return Me.tokens.Item(Me.currentToken)
        End Function

        Friend Sub RestoreCurrentToken(ByVal tokenValue As Integer)
            Me.currentToken = tokenValue
        End Sub

        Friend Function SaveCurrentToken() As Integer
            Return Me.currentToken
        End Function

        Friend Sub SetConstructorCompletions(ByVal computedType As Type, ByVal thisType As Type)
            Dim constructorBindingFlags As BindingFlags = (BindingFlags.FlattenHierarchy Or (BindingFlags.Public Or BindingFlags.Instance))
            If (computedType.Assembly Is thisType.Assembly) Then
                constructorBindingFlags = (constructorBindingFlags Or BindingFlags.NonPublic)
            End If
            Dim targetTypes As New List([Of] Type)(1) { computedType }
            Me.completions = RuleValidation.GetConstructors(targetTypes, constructorBindingFlags)
        End Sub

        Friend Sub SetMethodCompletions(ByVal computedType As Type, ByVal thisType As Type, ByVal methodName As String, ByVal includeStatic As Boolean, ByVal includeInstance As Boolean, ByVal validation As RuleValidation)
            Dim public As BindingFlags = BindingFlags.Public
            If (computedType.Assembly Is thisType.Assembly) Then
                [public] = ([public] Or BindingFlags.NonPublic)
            End If
            If includeInstance Then
                [public] = ([public] Or BindingFlags.Instance)
            End If
            If includeStatic Then
                [public] = ([public] Or (BindingFlags.FlattenHierarchy Or BindingFlags.Static))
            End If
            Dim candidateMethods As New List([Of] MemberInfo)
            Dim methods As MemberInfo() = computedType.GetMember(methodName, MemberTypes.Method, [public])
            ParserContext.AddCandidates(candidateMethods, methods)
            If computedType.IsInterface Then
                Dim list2 As New List([Of] Type)
                list2.AddRange(computedType.GetInterfaces)
                Dim i As Integer
                For i = 0 To list2.Count - 1
                    methods = list2.Item(i).GetMember(methodName, MemberTypes.Method, [public])
                    ParserContext.AddCandidates(candidateMethods, methods)
                    Dim interfaces As Type() = list2.Item(i).GetInterfaces
                    If (interfaces.Length > 0) Then
                        list2.AddRange(interfaces)
                    End If
                Next i
                methods = GetType(Object).GetMember(methodName, MemberTypes.Method, [public])
                ParserContext.AddCandidates(candidateMethods, methods)
            End If
            Dim info As ExtensionMethodInfo
            For Each info In validation.ExtensionMethods
                Dim error As ValidationError
                If ((info.Name = methodName) AndAlso RuleValidation.TypesAreAssignable(computedType, info.AssumedDeclaringType, Nothing, [error])) Then
                    candidateMethods.Add(info)
                End If
            Next
            Me.completions = candidateMethods
        End Sub

        Friend Sub SetNamespaceCompletions(ByVal nsSym As NamespaceSymbol)
            Me.completions = nsSym.GetMembers
        End Sub

        Friend Sub SetNestedClassCompletions(ByVal computedType As Type, ByVal thisType As Type)
            Dim bindingAttr As BindingFlags = (BindingFlags.FlattenHierarchy Or (BindingFlags.Public Or BindingFlags.Instance))
            If (computedType.Assembly Is thisType.Assembly) Then
                bindingAttr = (bindingAttr Or BindingFlags.NonPublic)
            End If
            Dim list As New List([Of] MemberInfo)(computedType.GetMembers(bindingAttr))
            Dim dictionary As New Dictionary([Of] String, MemberInfo)
            Dim info As MemberInfo
            For Each info In list
                If (Not info Is Nothing) Then
                    Dim memberType As MemberTypes = info.MemberType
                    If (((memberType = MemberTypes.TypeInfo) OrElse (memberType = MemberTypes.NestedType)) AndAlso ((info.DeclaringType Is thisType) OrElse ParserContext.IsNonPrivate(DirectCast(info, Type), thisType))) Then
                        dictionary.Item(info.Name) = info
                    End If
                End If
            Next
            Me.completions = dictionary.Values
        End Sub

        <SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")> _
        Friend Sub SetTypeMemberCompletions(ByVal computedType As Type, ByVal thisType As Type, ByVal isStatic As Boolean, ByVal validation As RuleValidation)
            Dim public As BindingFlags = BindingFlags.Public
            If isStatic Then
                [public] = ([public] Or (BindingFlags.FlattenHierarchy Or BindingFlags.Static))
            Else
                [public] = ([public] Or BindingFlags.Instance)
            End If
            If (computedType.Assembly Is thisType.Assembly) Then
                [public] = ([public] Or BindingFlags.NonPublic)
            End If
            Dim list As New List([Of] MemberInfo)(computedType.GetMembers([public]))
            If computedType.IsInterface Then
                Dim list2 As New List([Of] Type)(computedType.GetInterfaces)
                Dim i As Integer
                For i = 0 To list2.Count - 1
                    Dim type As Type = list2.Item(i)
                    list2.AddRange(type.GetInterfaces)
                    list.AddRange(type.GetMembers([public]))
                Next i
                list.AddRange(GetType(Object).GetMembers([public]))
            End If
            Dim info As ExtensionMethodInfo
            For Each info In validation.ExtensionMethods
                Dim error As ValidationError
                If RuleValidation.TypesAreAssignable(computedType, info.AssumedDeclaringType, Nothing, [error]) Then
                    list.Add(info)
                End If
            Next
            Dim dictionary As New Dictionary([Of] String, MemberInfo)
        Label_02DC:
            Dim info2 As MemberInfo
            For Each info2 In list
                Dim info3 As MethodInfo
                Dim info4 As PropertyInfo
                If (Not info2 Is Nothing) Then
                    Select Case info2.MemberType
                        Case MemberTypes.Property
                            info4 = DirectCast(info2, PropertyInfo)
                            Dim indexParameters As ParameterInfo() = info4.GetIndexParameters
                            If ((indexParameters Is Nothing) OrElse (indexParameters.Length <= 0)) Then
                                goto Label_0273
                            End If
                            Dim info5 As MethodInfo
                            For Each info5 In info4.GetAccessors((([public] And BindingFlags.NonPublic) <> BindingFlags.Default))
                                If ((info5.DeclaringType Is thisType) OrElse ParserContext.IsNonPrivate(info5, thisType)) Then
                                    dictionary.Item(info5.Name) = info5
                                End If
                            Next
                            [Continue]
                        Case MemberTypes.TypeInfo, MemberTypes.NestedType
                            If (isStatic AndAlso ((info2.DeclaringType Is thisType) OrElse ParserContext.IsNonPrivate(DirectCast(info2, Type), thisType))) Then
                                dictionary.Item(info2.Name) = info2
                            End If
                            [Continue]
                        Case MemberTypes.Field
                            goto Label_01CE
                        Case MemberTypes.Method
                            goto Label_0141
                    End Select
                End If
                [Continue] For
            Label_0141:
                info3 = DirectCast(info2, MethodInfo)
                If ((Not info3.IsSpecialName AndAlso Not info3.IsGenericMethod) AndAlso (((info3.DeclaringType Is thisType) OrElse ParserContext.IsNonPrivate(info3, thisType)) OrElse TypeOf info3 Is ExtensionMethodInfo)) Then
                    dictionary.Item(info2.Name) = info2
                End If
                [Continue] For
            Label_01CE:
                If ((info2.DeclaringType Is thisType) OrElse ParserContext.IsNonPrivate(DirectCast(info2, FieldInfo), thisType)) Then
                    dictionary.Item(info2.Name) = info2
                End If
                [Continue] For
            Label_0273:
                If (info2.DeclaringType Is thisType) Then
                    dictionary.Item(info2.Name) = info2
                Else
                    Dim info6 As MethodInfo
                    For Each info6 In info4.GetAccessors((([public] And BindingFlags.NonPublic) <> BindingFlags.Default))
                        If ParserContext.IsNonPrivate(info6, thisType) Then
                            dictionary.Item(info2.Name) = info2
                            goto Label_02DC
                        End If
                    Next
                End If
            Next
            Me.completions = dictionary.Values
        End Sub


        ' Properties
        Friend ReadOnly Property CurrentToken As Token
            Get
                If (Me.currentToken >= Me.tokens.Count) Then
                    Return Nothing
                End If
                Return Me.tokens.Item(Me.currentToken)
            End Get
        End Property

        Friend ReadOnly Property NumTokens As Integer
            Get
                Return Me.tokens.Count
            End Get
        End Property


        ' Fields
        Friend completions As ICollection
        Private currentToken As Integer
        Friend exprPositions As Dictionary([Of] Object, Integer)
        Friend provideIntellisense As Boolean
        Private tokens As List([Of] Token)
    End Class
End Namespace

